package org.opens.netty.agreement.custom.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;
import org.opens.netty.agreement.custom.message.Message;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

/**
 * Netty认为MessageToMessageCodec的子类接受到的结果都是完整的, 也就是不会出存储中间状态的, 所以是可以被共享的.
 */
@ChannelHandler.Sharable
@Slf4j
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> out) throws Exception {
        ByteBuf buffer = ctx.alloc().buffer();

        // 魔数: 4个字节
        buffer.writeBytes(new byte[]{1, 2, 3, 4});

        // 版本信息: 1字节
        buffer.writeByte(1);

        // 序列化算法: 1字节
        // 目前先使用jdk的序列化算法, 可以用动态的值来决定最终采用的算法: 0表示jdk, 1表示json
        buffer.writeByte(0);

        // 指令类型: 1字节
        buffer.writeByte(msg.getMessageType());

        // 指令序号: 4个字节
        buffer.writeInt(msg.getSequenceId());

        // 补全长度的字节: 如果最终数据的长度不是2的整数倍, 都不太专业, 所以需要对齐填充, 值无意义
        buffer.writeByte(0xFF);

        // 使用jdk的序列化算法将java对象转换为字节数组
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        // 这里只能转换实现了Serializable接口的对象
        oos.writeObject(msg);
        byte[] content = bos.toByteArray();
        // 正文长度: 4个字节
        buffer.writeInt(content.length);

        // 消息正文: 可变长度
        buffer.writeBytes(content);

        out.add(buffer);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 读取4个字节的魔数
        int magicNumber = in.readInt();

        // 读取1个字节的版本
        byte version = in.readByte();

        // 序列化算法
        byte serializerType = in.readByte();

        // 指令类型
        byte messageType = in.readByte();

        // 指令序号
        int sequenceId = in.readInt();

        // 对齐字段
        in.readByte();

        // 正文长度
        int length = in.readInt();

        byte[] result = new byte[length];
        // 此方法会将in中0到length的内容读取到第一个参数指定的byte[]中
        in.readBytes(result, 0, length);

        // 判断序列化算法类型
        if (serializerType == 0) {
            ByteArrayInputStream bis = new ByteArrayInputStream(result);
            ObjectInputStream ois = new ObjectInputStream(bis);
            Message message = (Message) ois.readObject();
            log.info("decode: {} {} {} {} {} {}", magicNumber, version, serializerType, messageType, sequenceId, length);
            log.info("decode message: {}", message);

            // 将解析的结果传递到下一个handle中
            out.add(message);
        }
    }
}
